import csv
from typing import Any
from enum import Enum
from datetime import datetime
from tzlocal import get_localzone_name
from collections import OrderedDict

from bqgraph.qt import QtWidgets, QtGui, QtCore, pyqtSignal
from ds.event import Event
from bqgraph.functions import safe_unicode
# from locale_back import _

DIRECTION_NONE = "无方向"
DIRECTION_LONG = "买"
DIRECTION_SHORT = "卖"
DIRECTION_UNKNOWN = "未知"
DIRECTION_NET = "净"

EVENT_IID_CHANGED = "eIIDChanged."

class Direction(Enum):
    """
    Direction of order/trade/position.
    """

    LONG = "多"
    SHORT = "空"
    NET = "净"


COLOR_LONG = QtGui.QColor("red")
COLOR_SHORT = QtGui.QColor("green")
COLOR_BID = QtGui.QColor(255, 174, 201)
COLOR_ASK = QtGui.QColor(160, 255, 160)
COLOR_BLACK = QtGui.QColor("black")


class StatusComboBox(QtWidgets.QComboBox):
    """状态栏的 ComboBox 单元格"""

    def __init__(
        self,
        name,
        label=None,
        data_event_engine=None,
        event_type=None,
        data_member=None,
        visible=True,
    ):
        """

        :param name: 控件名称
        :param label: 显示固定部分
        :param data_event_engine: 引起显示可变部分事件引擎，如果为None,仅显示固定部分
        :event_type: 事件类型
        """
        super(StatusComboBox, self).__init__()
        self.name = name
        self.setObjectName(name)
        self.label = label if label else ""
        self.data_event_engine = data_event_engine
        self.event_type = event_type
        self.data_member = data_member
        self.setVisible(visible)
        self.register()
        self.setEditable(False)
        self.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
        self.old_width = 0
        self.old_min_width = self.minimumWidth()
        # self.resize(100, 30)
        # print('StatusComboBox height:', self.height())

    def register(self):
        if self.data_event_engine and self.event_type:
            self.data_event_engine.register(self.event_type, self.update_event)

    def update_event(self, event):
        data = event.dict_["data"]
        if self.data_member:
            self.addItem(":".join([self.label, data.__dict__[self.data_member]]))
        else:
            self.addItem(":".join([self.label, data]))

    # ########以下为QComboBox 部分事件重载
    def activated(self, index: int):
        print(self.width())
        self.old_width = self.width()
        self.old_min_width = self.minimumWidth()
        self.setMaximumWidth(10000)
        self.setMinimumWidth(1000)
        print(self.width())

    def focusOutEvent(self, e: QtGui.QFocusEvent):
        self.setMinimumWidth(self.old_min_width)

    # #########  QComboBox 部分事件重载 结束


class StatusList(QtWidgets.QListWidget):
    """状态栏的list单元格"""

    def __init__(
        self,
        name,
        label=None,
        data_event_engine=None,
        event_type=None,
        data_member=None,
        visible=True,
    ):
        """

        :param name: 控件名称
        :param label: 显示固定部分
        :param data_event_engine: 引起显示可变部分事件引擎，如果为None,仅显示固定部分
        :event_type: 事件类型
        """
        super(StatusList, self).__init__()
        self.name = name
        self.setObjectName(name)
        self.label = label if label else ""
        self.data_event_engine = data_event_engine
        self.event_type = event_type
        self.data_member = data_member
        self.setVisible(visible)
        self.register()
        self.resize(100, 30)
        self.setEnabled(False)
        print("StatusList height:", self.height())

    def register(self):
        if self.data_event_engine and self.event_type:
            self.data_event_engine.register(self.event_type, self.update_event)

    def update_event(self, event):
        data = event.dict_["data"]
        if self.data_member:
            self.addItem(":".join([self.label, data.__dict__[self.data_member]]))
        else:
            self.addItem(":".join([self.label, data]))


class StatusCell(QtWidgets.QLineEdit):
    """状态栏的单元格"""

    def __init__(
        self,
        name,
        label=None,
        data_event_engine=None,
        event_type=None,
        data_member=None,
        visible=True,
    ):
        """

        :param name: 控件名称
        :param label: 显示固定部分
        :param data_event_engine: 引起显示可变部分事件引擎，如果为None,仅显示固定部分
        :event_type: 事件类型
        """
        super(StatusCell, self).__init__()
        self.name = name
        self.setObjectName(name)
        self.label = label if label else ""
        self.data_event_engine = data_event_engine
        self.event_type = event_type
        self.data_member = data_member
        self.register()
        self.setVisible(visible)
        if self.label:
            self.setText(self.label)

        self.setEnabled(False)

    def register(self):
        if self.data_event_engine and self.event_type:
            self.data_event_engine.register(self.event_type, self.update_event)

    def update_event(self, event):
        data = event.dict_["data"]
        # print(data)
        if not (isinstance(data, dict) or isinstance(data, (bool, int, float, str))):
            data = data.__dict__
        if isinstance(data, bool):
            if data:
                data = "1"
            else:
                data = "0"
        if self.data_member:
            self.setText(
                "".join([self.label, str(safe_unicode(data[self.data_member]))])
            )
        else:
            self.setText("".join([self.label, str(safe_unicode(data))]))


class BaseCell(QtWidgets.QTableWidgetItem):
    """
    General cell used in tablewidgets.
    """

    def __init__(self, content: Any, data: Any) -> None:
        """"""
        super().__init__()
        self.setTextAlignment(QtCore.Qt.AlignCenter)
        self.set_content(content, data)

    def set_content(self, content: Any, data: Any) -> None:
        """
        Set text content.
        """
        self.setText(str(content))
        self._data = data

    def get_data(self) -> Any:
        """
        Get data object.
        """
        return self._data


class EnumCell(BaseCell):
    """
    Cell used for showing enum data.
    """

    def __init__(self, content: str, data: Any) -> None:
        """"""
        super().__init__(content, data)

    def set_content(self, content: Any, data: Any) -> None:
        """
        Set text using enum.constant.value.
        """
        if content:
            super().set_content(content.value, data)


class DirectionCell(EnumCell):
    """
    Cell used for showing direction data.
    """

    def __init__(self, content: str, data: Any) -> None:
        """"""
        super().__init__(content, data)

    def set_content(self, content: Any, data: Any) -> None:
        """
        Cell color is set according to direction.
        """
        super().set_content(content, data)

        if content is Direction.SHORT:
            self.setForeground(COLOR_SHORT)
        else:
            self.setForeground(COLOR_LONG)


class BidCell(BaseCell):
    """
    Cell used for showing bid price and volume.
    """

    def __init__(self, content: Any, data: Any) -> None:
        """"""
        super().__init__(content, data)

        self.setForeground(COLOR_BID)


class AskCell(BaseCell):
    """
    Cell used for showing ask price and volume.
    """

    def __init__(self, content: Any, data: Any) -> None:
        """"""
        super().__init__(content, data)

        self.setForeground(COLOR_ASK)


class PnlCell(BaseCell):
    """
    Cell used for showing pnl data.
    """

    def __init__(self, content: Any, data: Any) -> None:
        """"""
        super().__init__(content, data)

    def set_content(self, content: Any, data: Any) -> None:
        """
        Cell color is set based on whether pnl is
        positive or negative.
        """
        super().set_content(content, data)

        if str(content).startswith("-"):
            self.setForeground(COLOR_SHORT)
        else:
            self.setForeground(COLOR_LONG)

from zoneinfo import ZoneInfo
class TimeCell(BaseCell):
    """
    Cell used for showing time string from datetime object.
    """

    local_tz = ZoneInfo(get_localzone_name())

    def __init__(self, content: Any, data: Any) -> None:
        """"""
        super().__init__(content, data)

    def set_content(self, content: Any, data: Any) -> None:
        """"""
        if content is None:
            return

        content: datetime = content.astimezone(self.local_tz)
        timestamp: str = content.strftime("%H:%M:%S")

        millisecond: int = int(content.microsecond / 1000)
        if millisecond:
            timestamp = f"{timestamp}.{millisecond}"
        else:
            timestamp = f"{timestamp}.000"

        self.setText(timestamp)
        self._data = data


class DateCell(BaseCell):
    """
    Cell used for showing date string from datetime object.
    """

    def __init__(self, content: Any, data: Any) -> None:
        """"""
        super().__init__(content, data)

    def set_content(self, content: Any, data: Any) -> None:
        """"""
        if content is None:
            return

        self.setText(content.strftime("%Y-%m-%d"))
        self._data = data


class MsgCell(BaseCell):
    """
    Cell used for showing msg data.
    """

    def __init__(self, content: str, data: Any) -> None:
        """"""
        super().__init__(content, data)
        self.setTextAlignment(QtCore.Qt.AlignLeft | QtCore.Qt.AlignVCenter)


class BasicCell(QtWidgets.QTableWidgetItem):
    """基础的单元格"""

    def __init__(self, text=None, data_event_engine=None):
        """Constructor"""
        super(BasicCell, self).__init__()
        self.data = None
        self.data_event_engine = data_event_engine
        if text:
            self.setContent(text)

    def setContent(self, text):
        """设置内容"""
        if text == "0" or text == "0.0":
            self.setText("")
        else:
            self.setText(str(text))


class CheckCell(QtWidgets.QTableWidgetItem):
    """check单元格"""

    def __init__(self, text=None, data_event_engine=None):
        """Constructor"""
        super(CheckCell, self).__init__()
        self.data = None
        self.data_event_engine = data_event_engine
        self.setCheckState(QtCore.Qt.Unchecked)
        if text:
            self.setContent(text)

    def setContent(self, text):
        """设置内容"""
        if text == "0" or text == "0.0":
            self.setText("")
        else:
            self.setText(str(text))


class ComboBoxCell(QtWidgets.QComboBox):
    """可设置为QTableWidgetItem的下拉框"""

    def __init__(self, items):
        super(ComboBoxCell, self).__init__()
        self.items = items

        if items:
            self.setContent(items)

    def setContent(self, items):
        for item in items:
            self.addItem(item)


class SpinCell(QtWidgets.QSpinBox):
    """可设置为QTableWidgetItem的QSpinBox控件"""

    def __init__(self, text=None, mainEngine=None):
        """Constructor"""
        super(SpinCell, self).__init__()
        # self.data = None
        # self.setRange(0,1000)
        # if text:
        #     self.setContent(text)

    def setContent(self, text):
        """设置内容"""

        if text == "0" or text == "0.0":
            self.setValue(0)
        else:
            self.setValue(int(text))
        # print(int(text))


########################################################################
class NameCell(QtWidgets.QTableWidgetItem):
    """用来显示合约中文的单元格"""

    def __init__(self, text=None, data_event_engine=None):
        """Constructor"""
        super(NameCell, self).__init__()

        self.data_event_engine = data_event_engine
        self.data = None

        if text:
            self.setContent(text)

    def setContent(self, text):
        """设置内容"""
        # print(text)
        if self.data_event_engine and text:
            # 首先尝试正常获取合约对象
            contract = self.data_event_engine.getContract(text)

            # 如果能读取合约信息
            if contract:
                if isinstance(contract, dict):
                    self.setText(contract["name"])


class BasicMonitor(QtWidgets.QTableWidget):
    """
    基础监控

    headerDict中的值对应的字典格式如下
    {'chinese': '中文名', 'cellType': BasicCell}

    """

    signal = pyqtSignal(type(Event))

    def __init__(
        self, mainEngine=None, eventEngine=None, parent=None, quoteEventEngine=None
    ):
        """Constructor"""
        super(BasicMonitor, self).__init__(parent)

        self.mainEngine = mainEngine
        self.eventEngine = eventEngine
        self.quoteEventEngine = quoteEventEngine

        # 保存表头标签用
        self.headerDict = OrderedDict()  # 有序字典，key是英文名，value是对应的配置字典
        self.headerList = []  # 对应self.headerDict.keys()

        # 保存相关数据用
        self.dataDict = {}  # 字典，key是字段对应的数据，value是保存相关单元格的字典
        self.dataKey = ""  # 字典键对应的数据字段

        # 监控的事件类型
        self.eventType = ""

        # 字体
        self.font = None

        # 保存数据对象到单元格
        self.saveData = False

        # 默认不允许根据表头进行排序，需要的组件可以开启
        self.sorting = False

        # 初始化右键菜单
        self.menu = None
        self.initMenu()

    def setHeaderDict(self, headerDict):
        """设置表头有序字典"""
        self.headerDict = headerDict
        self.headerList = headerDict.keys()

    def setDataKey(self, dataKey):
        """设置数据字典的键"""
        self.dataKey = dataKey

    def setEventType(self, eventType):
        """设置监控的事件类型"""
        self.eventType = eventType

    def setFont(self, font):
        """设置字体"""
        self.font = font

    def setSaveData(self, saveData):
        """设置是否要保存数据到单元格"""
        self.saveData = saveData

    def initTable(self):
        """初始化表格"""
        # 设置表格的列数
        col = len(self.headerDict)
        self.setColumnCount(col)

        # 设置列表头
        labels = [d["chinese"] for d in self.headerDict.values()]
        self.setHorizontalHeaderLabels(labels)

        # 关闭左边的垂直表头
        self.verticalHeader().setVisible(False)

        # 设为不可编辑
        self.setEditTriggers(self.NoEditTriggers)

        # 设为行交替颜色
        self.setAlternatingRowColors(True)

        # 设置允许排序
        self.setSortingEnabled(self.sorting)

    def registerEvent(self):
        """注册GUI更新相关的事件监听"""
        # print('注册GUI更新相关的事件监听')
        self.signal.connect(self.updateEvent)
        self.eventEngine.register(self.eventType, self.signal.emit)
        if self.quoteEventEngine:
            self.quoteEventEngine.register(self.eventType, self.signal.emit)

    def updateEvent(self, event):
        """收到事件更新"""
        data = event.dict_["data"]
        # print(data)

        self.updateData(data)

    def updateData(self, data):
        """将数据更新到表格中"""
        # 如果允许了排序功能，则插入数据前必须关闭，否则插入新的数据会变乱
        if self.sorting:
            self.setSortingEnabled(False)

        # 如果设置了dataKey，则采用存量更新模式
        if self.dataKey:
            key = data.__getattribute__(self.dataKey)
            # 如果键在数据字典中不存在，则先插入新的一行，并创建对应单元格
            if key not in self.dataDict:
                self.insertRow(0)
                d = {}
                for n, header in enumerate(self.headerList):
                    content = safe_unicode(data.__getattribute__(header))
                    cellType = self.headerDict[header]["cellType"]
                    cell = cellType(content, self.mainEngine)

                    if self.font:
                        cell.setFont(self.font)  # 如果设置了特殊字体，则进行单元格设置

                    if self.saveData:  # 如果设置了保存数据对象，则进行对象保存
                        cell.data = data

                    # self.setItem(0, n, cell)
                    # print(self.headerDict[header]['cellType'] is BasicCell)
                    if (
                        self.headerDict[header]["cellType"] is BasicCell
                        or self.headerDict[header]["cellType"] is NameCell
                        or self.headerDict[header]["cellType"] is BidCell
                        or self.headerDict[header]["cellType"] is AskCell
                        or self.headerDict[header]["cellType"] is DirectionCell
                    ):
                        self.setItem(0, n, cell)
                    else:
                        # print(self.headerDict[header]['cellType'])
                        self.setCellWidget(0, n, cell)

                    d[header] = cell
                self.dataDict[key] = d
            # 否则如果已经存在，则直接更新相关单元格
            else:
                d = self.dataDict[key]
                for header in self.headerList:
                    content = safe_unicode(data.__getattribute__(header))
                    cell = d[header]
                    cell.setContent(content)

                    if self.saveData:  # 如果设置了保存数据对象，则进行对象保存
                        cell.data = data
                        # 否则采用增量更新模式
        else:
            self.insertRow(0)

            for n, header in enumerate(self.headerList):

                content = safe_unicode(data.__getattribute__(header))

                cellType = self.headerDict[header]["cellType"]
                cell = cellType(content, self.mainEngine)  # , self.account_engine
                # print((n, header, content,cell.text()))

                if self.font:
                    cell.setFont(self.font)

                if self.saveData:
                    cell.data = data

                self.setItem(0, n, cell)

                # 调整列宽
        self.resizeColumns()

        # 重新打开排序
        if self.sorting:
            self.setSortingEnabled(True)

    def resizeColumns(self):
        """调整各列的大小"""
        self.horizontalHeader().resizeSections(QtWidgets.QHeaderView.ResizeToContents)

    def setSorting(self, sorting):
        """设置是否允许根据表头排序"""
        self.sorting = sorting

    def saveToCsv(self):
        """保存表格内容到CSV文件"""
        # 先隐藏右键菜单
        self.menu.close()

        # 获取想要保存的文件名
        path = QtWidgets.QFileDialog.getSaveFileName(self, "保存数据", "", "CSV(*.csv)")

        try:
            if path:
                with open(path, "wb") as f:
                    writer = csv.writer(f)

                    # 保存标签
                    headers = [header.encode("gbk") for header in self.headerList]
                    writer.writerow(headers)

                    # 保存每行内容
                    for row in range(self.rowCount()):
                        rowdata = []
                        for column in range(self.columnCount()):
                            item = self.item(row, column)
                            if item is not None:
                                rowdata.append(item.text().encode("gbk"))
                            else:
                                rowdata.append("")
                        writer.writerow(rowdata)
        except IOError:
            pass

    def initMenu(self):
        """初始化右键菜单"""
        self.menu = QtWidgets.QMenu(self)

        saveAction = QtWidgets.QAction("保存内容", self)
        saveAction.triggered.connect(self.saveToCsv)

        self.menu.addAction(saveAction)

    def contextMenuEvent(self, event):
        """右键点击事件"""
        self.menu.popup(QtGui.QCursor.pos())


def create_check_menu_list(parent, items):
    """
    创建选择型菜单
    :param parent:
    :param items: {'name':{'display':'display','checked':1}}
    :return:
    """
    rtn = {}
    for n, v in items.items():
        rtn[n] = QtWidgets.QAction(v["display"], parent)
        rtn[n].setObjectName(n)
        rtn[n].setCheckable(True)
        rtn[n].setChecked(v["checked"] == 1)
    return rtn
